using System.Collections;
using NFreeMarker.Template;

namespace NFreeMarker.Ext.Beans
{
    /**
     * <p>A class that adds {@link TemplateModelIterator} functionality to the
     * {@link Enumeration} interface implementers. 
     * </p> <p>Using the model as a collection model is NOT thread-safe, as 
     * enumerations are inherently not thread-safe.
     * Further, you can iterate over it only once. Attempts to call the
     * {@link #iterator()} method after it was already driven to the end once will 
     * throw an exception.</p>
     * @author Attila Szegedi
     * @version $Id: EnumerationModel.java,v 1.24 2003/06/03 13:21:32 szegedia Exp $
     */
    public class EnumerationModel : BeanModel, ITemplateModelIterator, ITemplateCollectionModel
    {
        private bool _accessed;
        private bool _hasNext;
        
        /**
         * Creates a new model that wraps the specified enumeration object.
         * @param enumeration the enumeration object to wrap into a model.
         * @param wrapper the {@link BeansWrapper} associated with this model.
         * Every model has to have an associated {@link BeansWrapper} instance. The
         * model gains many attributes from its wrapper, including the caching 
         * behavior, method exposure level, method-over-item shadowing policy etc.
         */
        public EnumerationModel(IEnumerator enumeration, BeansWrapper wrapper)
            : base(enumeration, wrapper)
        {
            _hasNext = enumeration.MoveNext();
        }

        /**
         * This allows the enumeration to be used in a <tt>&lt;foreach></tt> block.
         * @return "this"
         */
        public ITemplateModelIterator Iterator()
        {
            lock (this)
            {
                if (_accessed)
                {
                    throw new TemplateModelException(
                        "This collection is stateful and can not be iterated over the" +
                        " second time.");
                }
                _accessed = true;
            }
            return this;
        }
        
        /**
         * Calls underlying {@link Enumeration#nextElement()}.
         */
        public bool HasNext()
        {
            return _hasNext;
        }

        /**
         * Calls underlying {@link Enumeration#nextElement()} and wraps the result.
         */
        public ITemplateModel Next()
        {
            if (!HasNext())
            {
                throw new TemplateModelException(
                    "No more elements in the enumeration.");
            }

            object value = ((IEnumerator) Object).Current;
            _hasNext = ((IEnumerator) Object).MoveNext();

            return Wrap(value);
        }

        /**
         * Returns {@link Enumeration#hasMoreElements()}. Therefore, an
         * enumeration that has no more element evaluates to false, and an 
         * enumeration that has further elements evaluates to true.
         */
        public bool GetAsBoolean()
        {
            return HasNext();
        }
    }
}